/* FILE : server.c */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "server.h"

#define DEFAULT_LISTEN_PORT 8000
#define SERVER_BUFFER_LENGTH 16384

int listen_port = DEFAULT_LISTEN_PORT;
int temp_sock_descriptor = 0;
char buffer[SERVER_BUFFER_LENGTH];

pid_t child = -1;
ServerReceiveHandler rec_handler = NULL;
int pipes[2];

static void on_server_exit(int);
static void socket_loop(int, int);
static void pipe_read(int);
static void pipe_write(char *, int);
static void server_send_buffer(void *, size_t);

int
server_start()
{
	int ret;
	ret = pipe(pipes);
	if (ret == -1) {
		fprintf(stderr, "XAUX : cannot create pipes\n");
		return 0;
	}
	child = fork();

	if (child < 0) {
		fprintf(stderr, "XAUX : cannot fork for socket server\n");
		return 0;
	} else if (child == 0) {
		signal(SIGUSR1, pipe_read);
		server_set_receive_handler(server_send_buffer);
		socket_loop(pipes[0], pipes[1]);
	} else {
		signal(SIGUSR1, pipe_read);
		signal(SIGCHLD, on_server_exit);
	}
	return 1;
}

void
server_stop()
{
	close(pipes[0]);
	close(pipes[1]);
	kill(child, SIGTERM);
}

int
server_restart()
{
	server_stop();
	if (server_start())
		return 1;
	else
		return 0;
}

void
server_send(void *buf, size_t buf_size)
{
#if DEBUG
	fprintf(stderr, "XAUX : server send first buf int : %d\n", *(int *)buf);
#endif
	kill(child, SIGUSR1);
	pipe_write((char *)buf, (int)buf_size);
}

void
server_set_receive_handler(ServerReceiveHandler handler)
{
	rec_handler = handler;
}

void
server_set_listen_port(int port)
{
	listen_port = port;
}

void
server_unset_listen_port(int port)
{
	listen_port = DEFAULT_LISTEN_PORT;
}

static void
on_server_exit(int signo)
{
	waitpid(child, NULL, 0);
	child = -1;
}

static void
socket_loop(int read_pipe, int write_pipe)
{
	struct sockaddr_in sin;
	struct sockaddr_in pin;
	int sock_descriptor;
	int address_size;
	char buf[SERVER_BUFFER_LENGTH];
	int len = SERVER_BUFFER_LENGTH;

	sock_descriptor = socket(AF_INET, SOCK_STREAM, 0);
	if (sock_descriptor == -1) {
#if DEBUG
		fprintf(stderr, "XAUX : cannot create socket\n");
#endif
		exit(1);
	}

	bzero(&sin, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = htons(listen_port);

	if (bind(sock_descriptor,
			(struct sockaddr *)&sin,
			sizeof(sin))
			== -1) {
#if DEBUG
		fprintf(stderr, "XAUX : cannot bind socket\n");
#endif
		exit(1);
	}

	if (listen(sock_descriptor, 20) == -1) {
#if DEBUG
		fprintf(stderr, "XAUX : cannot listen socket\n");
#endif
		exit(1);
	}

#if DEBUG
	fprintf(stderr, "Accepting connections ... \n");
#endif

	while(1) {
		temp_sock_descriptor =
			accept(sock_descriptor,
			(struct sockaddr *)&pin,
			&address_size);

		if (temp_sock_descriptor == -1) {
#if DEBUG
			fprintf(stderr, "XAUX : accept connection error\n");
#endif
			exit(1);
		}

#if DEBUG
		fprintf(stderr, "XAUX : a client had connect\n");
#endif

		if (recv(temp_sock_descriptor,
				buf,
				len,
				0) == -1) {
#if DEBUG
			fprintf(stderr, "XAUX : connection receive error\n");
#endif
			exit(1);
		}

		kill(getppid(), SIGUSR1);
		pipe_write(buf, len);
	}
}

static void
pipe_read(int signo)
{
	int len;
	char buf[SERVER_BUFFER_LENGTH];

	read(pipes[0], &len, sizeof(int));
#if DEBUG
	fprintf(stderr, "XAUX : pipe read size : %d\n", len);
#endif
	read(pipes[0], buf, sizeof(char)*len);

	if (rec_handler)
		(*rec_handler)(buf, len);
}

static void
pipe_write(char *buf, int len)
{
	write(pipes[1], &len, sizeof(int));

	write(pipes[1], buf, sizeof(char)*len);
}

static void
server_send_buffer(void *buf, size_t buf_size)
{
#if DEBUG
	fprintf(stderr, "XAUX : server send buffer\n");
#endif
	send(temp_sock_descriptor, (char *)buf, (int)buf_size, 0);
}
